﻿#region License
/* ---------------------------------------------------------------- *
 *
 * This file is part of the Xcoordination Application Space
 * ("XcoAppSpace") http://www.xcoordination.com
 *
 * Copyright (C) 2009 Xcoordination http://www.xcoordination.com
 *
 * XcoAppSpace is free software; you can redistribute it and/or
 * modify it under the terms of version 2 of the GNU General
 * Public License as published by the Free Software Foundation.
 *
 * XcoAppSpace is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program. If not, see http://www.gnu.org/licenses/
 * or write to the Free Software Foundation, Inc., 51 Franklin
 * Street, Fifth Floor, Boston, MA 02110-1301, USA.
 *
 * ---------------------------------------------------------------- */
#endregion

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using XcoAppSpaces.Contracts.Exceptions;
using XcoAppSpaces.Contracts.Service;

namespace XcoAppSpaces.Core.Configuration
{
	class ServiceRegistry : IXcoServiceRegistry
	{
		private readonly List<XcoServiceConfiguration> registeredServices = new List<XcoServiceConfiguration>();
		
		public IXcoAppSpace Space { get; set; }

		public ServiceRegistry(IXcoAppSpace space)
		{
			this.Space = space;
		}
        
		#region IXcoServiceRegistry Members

		public List<XcoServiceConfiguration<T>> ResolveAll<T>() where T : IXcoService
		{
			return registeredServices.Where(s => s.Service is T).Select(s => AsConfig<T>(s)).ToList();
		}

		public XcoServiceConfiguration<T> AsConfig<T>(XcoServiceConfiguration config) where T : IXcoService
		{
			if (config is XcoServiceConfiguration<T>)
				return (XcoServiceConfiguration<T>) config;
			if (!(config.Service is T))
				throw new XcoConfigurationException("Unexpected service conversion error! Cannot convert " + config.Service + " to " + typeof(T));
			return new XcoServiceConfiguration<T>{Service = (T)config.Service, Name = config.Name, IsDefault = config.IsDefault, Mappings = config.Mappings}; 
		}

		public T Resolve<T>(string name) where T : IXcoService
		{
			var serviceInfo = registeredServices.FirstOrDefault(s => s.Name == name && s.Service is T);
			return (serviceInfo != null ? (T)serviceInfo.Service : default(T));
		}

		public T Resolve<T>() where T : IXcoService
		{
			var serviceInfo = registeredServices.FirstOrDefault(s => s.Service is T && s.IsDefault);
			return (serviceInfo != null ? (T)serviceInfo.Service : default(T));
		}

		public void InitializeServices()
		{
			for (int i = 0; i < registeredServices.Count; i++) //because list of services could change during initialization
			{
				registeredServices[i].Service.Initialize(this);
			}
		}

		public void Register<T>(T service) where T : IXcoService
		{
			registeredServices.Add(new XcoServiceConfiguration<T> { Service = service, Name = null, IsDefault = true});
		}

		public void Register(XcoServiceConfiguration service)
		{
			registeredServices.Add(service);
		}

		public void StartServices()
		{
			try
			{
				var services =
					registeredServices.Where(s => s.Service is IXcoStatefulService).Select(s => (IXcoStatefulService) s.Service).
						OrderBy(s => s.StartPriority);
				foreach (var service in services)
				{
					service.Start();
				}
			}
			catch (Exception)
			{
				StopServices();
				throw;
			}
		}

		public void StopServices()
		{
			//stop in certain order??
			foreach (var service in registeredServices.Where(s => s.Service is IXcoStatefulService).Select(s => (IXcoStatefulService)s.Service).OrderByDescending(s => s.StartPriority))
			{
				try
				{
					service.Stop();
				}
				catch (Exception)
				{
				}
			}
		}

		#endregion

		#region IDisposable Members

		public void Dispose()
		{
			StopServices();
		}

		#endregion
	}
}
